package com.core.util;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;

/**
 * Created by Jerry on 2017/4/24.
 */
public final class DateUtils {
    private static final String DATE_FORMAT_FULL_DATETIME = "EEE MMM dd HH:mm:ss Z yyyy";
    private static final String DATE_FORMAT_DATETIME = "yyyy-MM-dd HH:mm:ss";
    private static final String DATE_FORMAT_DATE = "yyyyMMdd";
    private static final String DATE_FORMAT_DATE_STRIKE = "yyyy-MM-dd";
    private static final String DATE_FORMAT_DATE_SLASH = "yyyy/MM/dd";
    private static final String DATE_FORMAT_DATE_FULL = "yyyyMMddHHmmss";
    private static int monthOffset;

    private DateUtils() {
    }

    public static int getMonthOffset() {
        return monthOffset;
    }

    public static void setMonthOffset(int monthOffset) {
        DateUtils.monthOffset = monthOffset;
    }

    public static String toString(Date date, String format) {
        SimpleDateFormat sdf = new SimpleDateFormat(format);
        return sdf.format(date);
    }

    public static String toDisplay(String dateStr) throws Exception {
        Date date = DateUtils.date(dateStr, DATE_FORMAT_DATE_FULL);

        return DateUtils.toString(date, DATE_FORMAT_DATETIME);
    }

    public static String displayNow() throws Exception {
        Date date = DateUtils.getDate();

        return DateUtils.toString(date, DATE_FORMAT_DATETIME);
    }

    public static Date date(String date, String format) throws Exception {
        SimpleDateFormat sdf = new SimpleDateFormat(format);
        return sdf.parse(date);
    }

    public static Date date(String date) throws Exception {
        if (!StringUtils.hasText(date) || "null".equalsIgnoreCase(date)) return null;

        String[] formats = new String[]{DATE_FORMAT_DATETIME, DATE_FORMAT_DATE, DATE_FORMAT_FULL_DATETIME, DATE_FORMAT_DATE_STRIKE, DATE_FORMAT_DATE_SLASH, DATE_FORMAT_DATE_FULL};
        SimpleDateFormat sdf = new SimpleDateFormat(findValidFormat(date, formats));
        return sdf.parse(date);
    }

    private static String findValidFormat(String value, String[] formats) {
        boolean isValid = false;
        SimpleDateFormat sdf;
        String pattern = formats[0];
        for (String format : formats) {
            if (isValid) break;
            try {
                sdf = new SimpleDateFormat(format);
                sdf.setLenient(false);
                sdf.parse(value);
                isValid = true;
                pattern = format;
            } catch (Exception e) {
                isValid = false;
            }
        }
        return pattern;
    }

    public static Date date(int year, int month, int day) {
        return date(year, month, day, 0, 0, 0);
    }

    public static Date date(int year, int month, int day, int hour, int minute, int second) {
        Calendar calendar = Calendar.getInstance();
        calendar.setLenient(false);
        calendar.set(year, month, day, hour, minute, second);
        calendar.set(Calendar.MILLISECOND, 0);
        return calendar.getTime();
    }

    public static Calendar calendar(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        return calendar;
    }

    public static Date add(Date date, int field, int value) {
        Calendar calendar = calendar(date);
        calendar.add(field, value);
        return calendar.getTime();
    }

    public static int get(Date date, int field) {
        Calendar calendar = calendar(date);
        return calendar.get(field);
    }

    public static Date withField(Date date, int field, int value) {
        Calendar calendar = calendar(date);
        calendar.set(field, value);
        return calendar.getTime();
    }

    public static int getYear(Date date) {
        return get(date, Calendar.YEAR);
    }

    public static int getMonth(Date date) {
        return get(date, Calendar.MONTH);
    }

    public static int getDay(Date date) {
        return get(date, Calendar.DATE);
    }

    public static int getHour(Date date) {
        return get(date, Calendar.HOUR_OF_DAY);
    }

    public static int getMinute(Date date) {
        return get(date, Calendar.MINUTE);
    }

    public static Date withHour(Date date, int value) {
        return withField(date, Calendar.HOUR_OF_DAY, value);
    }

    public static Date withMinute(Date date, int value) {
        return withField(date, Calendar.MINUTE, value);
    }

    public static Date toCurrentTimeZone(Date targetDate, TimeZone targetTimeZone) {
        Calendar target = calendar(targetDate);

        Calendar result = Calendar.getInstance(targetTimeZone);
        result.set(Calendar.YEAR, target.get(Calendar.YEAR));
        result.set(Calendar.MONTH, target.get(Calendar.MONTH));
        result.set(Calendar.DATE, target.get(Calendar.DATE));
        result.set(Calendar.HOUR_OF_DAY, target.get(Calendar.HOUR_OF_DAY));
        result.set(Calendar.MINUTE, target.get(Calendar.MINUTE));
        result.set(Calendar.SECOND, target.get(Calendar.SECOND));
        result.set(Calendar.MILLISECOND, target.get(Calendar.MILLISECOND));

        return result.getTime();
    }

    public static boolean isWeekDay(Date targetDate) {
        Calendar calendar = calendar(targetDate);
        int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK);
        return dayOfWeek != Calendar.SATURDAY && dayOfWeek != Calendar.SUNDAY;
    }

    public static int getWeekOfMonth(Date date) {
        Calendar ca = Calendar.getInstance();
        ca.setFirstDayOfWeek(Calendar.MONDAY);//设置一个星期的第一天，按中国的习惯一个星期的第一天是星期一
        ca.setTime(date);
        return ca.get(ca.WEEK_OF_MONTH);
    }

    public static int getMaxDateOfMonth(Date date) {
        Calendar ca = Calendar.getInstance();
        ca.setTime(date);
        return ca.getActualMaximum(Calendar.DAY_OF_MONTH);
    }

    public static Date getMaxDateOfWeek(Date date, int firstDay) {
        Calendar ca = Calendar.getInstance();
        ca.setFirstDayOfWeek(firstDay);
        ca.setTime(date);
        ca.set(Calendar.DAY_OF_WEEK, ca.getFirstDayOfWeek() + 6);
        return ca.getTime();
    }

    public static Date getMinDateOfWeek(Date date, int firstDay) {
        Calendar ca = Calendar.getInstance();
        ca.setFirstDayOfWeek(firstDay);
        ca.setTime(date);
        ca.set(Calendar.DAY_OF_WEEK, ca.getFirstDayOfWeek());
        return ca.getTime();
    }

    public static boolean isDateValid(int year, int month, int day) {
        try {
            date(year, month, day);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    public static Date truncateTime(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        return calendar.getTime();
    }

    public static int getAge(Date birthday, Date now) {
        int age = 0;
        if (birthday != null) {
            if (birthday.after(now)) {
                throw new IllegalArgumentException("年龄不能超过当前日期");
            }
            age = getYear(now) - getYear(birthday);
            if (getDay(now) < getDay(birthday)) {
                age -= 1;
            }
        }
        return age;
    }

    public static Date getDate() {
        return getDate(getMonthOffset());
    }

    private static Date getDate(int month) {
        return add(new Date(), Calendar.MONTH, month);
    }

}